/*
 * WorkLossApi.cpp
 *
 *  Created on: 2016-6-8
 *      Author: root
 */
/*
 * 直接发送20个包，去除前后各2，只针对中间16个计算
 *
 */

#include"WorkPingApi.h"
#include"WorkPriApi.h"
#ifndef NOUSE_PUBLIC_LOG
#include"PublicLog.h"
#endif
#if USE_PUBLIC_PING
#define SHOW_DEBUF_INFO 0
//#define MAX_PING_TIMES 20
#define DEFAULT_MAX_TIMES 20
typedef struct _PINGLOSSINFOTMP{
int  ping[DEFAULT_MAX_TIMES];
}PINGLOSSINFOTMP;
typedef std::map<std::string,PINGLOSSINFOTMP>MAPPINGLOSSINFOTMP;
MAPLOSSINFO GetLossStatusByTmp(MAPPINGLOSSINFOTMP tmpstatus,int MAX_PING_TIMES);
//IP列表 包大小 检测ID
//发送间隔  单次发送数量
//超时时间  最大个数
//构建周期距离上一次建表时间
MAPLOSSINFO static _GetLossInfoByIPList(SETIPLIST IPList,PINGFLAG flag);

MAPLOSSINFO GetLossInfoByIPMAPList(MAPIPLIST IPMapList,PINGFLAG flag){
	MAPLOSSINFO result;
	MAPIPLIST::iterator it=IPMapList.begin();
	while(it!=IPMapList.end()){
//判断是否还有探得必要		
		SETIPLIST IPList;
		if(result.empty()){
			IPList=it->second;
		}else{
			SETIPLIST::iterator itip=it->second.begin();
			while(itip!=it->second.end()){
				MAPLOSSINFO::iterator itresult=result.find(itip->c_str());
				if(itresult==result.end()){
					IPList.insert(itip->c_str());
				}else
				if((itresult->second.pingavg<1)||(itresult->second.pingavg>20)){
					IPList.insert(itip->c_str());
				}	
				itip++;
			}
		}
		if(IPList.empty()){
			WARN("PING 源IP:%s 已无需额外探测",it->first.c_str());
			it++;
			continue;
		}else{
			INFO("PING 要探测数量:%zd 过滤后探测数量:%zd",it->second.size(),IPList.size());
		}
		memset(flag.SrcIP,0,sizeof(flag.SrcIP));
		strcpy(flag.SrcIP,it->first.c_str());
		MAPLOSSINFO tmp=GetLossInfoByIPList(IPList,flag);
		if(result.empty()){
			result=tmp;
		}else{
			MAPLOSSINFO::iterator it2=tmp.begin();
			while(it2!=tmp.end()){
				MAPLOSSINFO::iterator itresult=result.find(it2->first.c_str());
				if(itresult==result.end()){
					result.insert(std::pair<std::string,LOSSINFO>(it2->first.c_str(),it2->second));
				}else
//上次没有值取本次					
				if(itresult->second.pingavg<1){
					itresult->second=it2->second;
				}else
//取丢包率小的					
				if(it2->second.pingdrop<itresult->second.pingdrop){
					itresult->second=it2->second;
				}else
//本次有值，取延时小的					
				if((it2->second.pingavg>0)&&(it2->second.pingavg<itresult->second.pingavg)){
					itresult->second=it2->second;
				}	
				it2++;
			}
		}
		it++;
    }
	return result;
}

MAPLOSSINFO GetLossInfoByIPList(SETIPLIST IPList,PINGFLAG flag){
    MAPLOSSINFO result;
    if(IPList.empty()){
		INFO("Ping 数量:%zd 为空 源IP:%s",IPList.size(),flag.SrcIP);
		return result;
    }
    ReSetStartTime();
    InitPingPID();
    if(flag.PingTimes<5)flag.PingTimes=5;
    if(flag.PingTimes>20)flag.PingTimes=20;
	if(flag.PacketSize<80)flag.PacketSize=80;
    ShowPingFlag(flag,IPList.size());
	if(flag.bSupportIPV6==1){
		result=_GetLossInfoByIPList(IPList,flag);
	}else{
		SETIPLIST RealIPList;
		SETIPLIST::iterator it=IPList.begin();
		while(it!=IPList.end()){
			if(IsIPV6(it->c_str())==0){
				RealIPList.insert(it->c_str());
			}
			it++;
		}
		result=_GetLossInfoByIPList(RealIPList,flag);
	}
    INFO("Ping 数量:%zd 结果数量:%zd CheckID:%hhu 源IP:%s 耗时:%d ms",IPList.size(),result.size(),flag.CheckID,flag.SrcIP,GetEndTime());
    return result;
}
MAPLOSSINFO GetLossInfoByIPListWithLocal(SETIPLIST IPList,PINGFLAG flag){
    SETIPLIST publiclist=GetPublicIPList();
    MAPLOSSINFO result;
//本地IP数低于2个走默认路由
    if(publiclist.size()<2){
		result=GetLossInfoByIPList(IPList,flag);
    }else{
		SETIPLIST::iterator it=publiclist.begin();
		while(it!=publiclist.end()){
			memset(flag.SrcIP,0,sizeof(flag.SrcIP));
			strcpy(flag.SrcIP,it->c_str());
			MAPLOSSINFO tmp=GetLossInfoByIPList(IPList,flag);
			MAPLOSSINFO::iterator it2=tmp.begin();
			while(it2!=tmp.end()){
//能PING通且丢包低于预估值队列移除
				if((it2->second.pingavg>0)&&(it2->second.pingavg<20)&&(it2->second.pingdrop<10)){
					IPList.erase(it2->first.c_str());
				}
				MAPLOSSINFO::iterator it3=result.find(it2->first.c_str());
				if(it3==result.end()){
					result.insert(std::pair<std::string,LOSSINFO>(it2->first.c_str(),it2->second));
				}else
				if(it2->second.pingdrop<it3->second.pingdrop){
					it3->second=it2->second;
				}else
				if((it2->second.pingavg>0)&&(it2->second.pingavg<it3->second.pingavg)){
					it3->second=it2->second;
				}
				it2++;
            }
			it++;
        }
    }
    return result;
}
#include"PublicRandom.h"
MAPLOSSINFO _GetLossInfoByIPList(SETIPLIST IPList,PINGFLAG flag){
	PublicRandom IPListTmp;
	IPListTmp.InsertSetList(IPList);
	MAPPINGLOSSINFOTMP   tmpstatus;
	SETIPPINGCHECK       checklist;
	int             sendflag=1;//发送标志
	int             checkflag=0;//检测标志
	int             makeflag=0;//构建标志
	int             sendtimes=0;
	struct timeval  sendtimestamp={0,0};
	struct timeval  checktimestamp={0,0};
	struct timeval  maketimestamp={0,0};
	int             sendnum=0;
	int				bStopRunning=0;//紧急停止
	CPublicSocket sock;
	if(sock.CreateSocket(SOCKET_TYPE_ICMP,flag.PerTime,flag.SrcIP)==-1){
		WRONG("Create Socket Error!!!");
		return GetLossStatusByTmp(tmpstatus,flag.PingTimes);
	}	
	gettimeofday(&sendtimestamp,NULL);
	checktimestamp=sendtimestamp;
	maketimestamp =sendtimestamp;
	while((sendtimes<flag.PingTimes)||(IPListTmp.empty()==false)||(checklist.empty()==false)){
//优先判断队列为空的情况
//队列为空且未到达指定次数
//补包操作		
		if((makeflag==1)&&(IPListTmp.empty()==true)&&(sendtimes<flag.PingTimes)){
			sendtimes++;
			if(sendtimes<flag.PingTimes){
				IPListTmp.InsertSetList(IPList);
			}
			makeflag=0;
			INFO("PING MakeLIST CheckID:%hhu Size:%zd 进度:%d MakeTime:%ld.%ld SendTime:%ld.%ld",
				flag.CheckID,IPListTmp.size(),sendtimes,
				maketimestamp.tv_sec,maketimestamp.tv_usec,
				sendtimestamp.tv_sec,sendtimestamp.tv_usec);
		}
//发送阶段判断		
//应该循环发送		
		while((sendflag==1)&&(IPListTmp.empty()==false)){
			sendflag=0;
			IPPINGCHECK checkinfo;
			memset(&checkinfo,0,sizeof(IPPINGCHECK));
			if(IPListTmp.empty()==false){
				std::string tmp=IPListTmp.PopList();
				strcpy(checkinfo.ip,tmp.c_str());
				checkinfo.seqinfo.flag.id=sendtimes;
				checkinfo.seqinfo.flag.check=flag.CheckID;
				int Ret=SendICMPPacket(&sock,flag.PacketSize,checkinfo);
#if SHOW_DEBUF_INFO				
				WARN("Send IP:%s ID:%hu Check:%hu Ret:%d",checkinfo.ip,
					checkinfo.seqinfo.flag.id,
					checkinfo.seqinfo.flag.check,Ret);
#endif				
				if(Ret==0){
					checklist.insert(checkinfo);
					sendnum++;
					if(sendnum<flag.PerNum){
						sendflag=1;
					}
				}else
				if(Ret==-1){
					//IPListTmp.PushList(checkinfo.ip);
					WRONG("发送失败 IP:%s 在GetLossInfoByIPList",checkinfo.ip);
				}else
				if(Ret==-2){
					WRONG("发送异常 IP:%s 在GetLossInfoByIPList",checkinfo.ip);
					//break;
				}else{
					WRONG("发现问题 IP:%s 在GetLossInfoByIPList",checkinfo.ip);
				}
				if(sendflag==0){
					sendnum=0;
					sendtimestamp=checkinfo.sendtime;
				}
			}else{
				WRONG("不应出现此调用　在GetLossInfoByIPList");
			}			
		}
//处理抓包	
		int usetime=0;
		do{
			char fromip[40]={0};
			IPPINGCHECK checkinfo;
			struct timeval timestamp;
			memset(&checkinfo,0,sizeof(IPPINGCHECK));
			memset(&timestamp,0,sizeof(struct timeval));
			usetime=ReadICMPPacket(&sock,flag.CheckID,checkinfo,timestamp,fromip);	
			if(usetime>-2){
#if SHOW_DEBUF_INFO				
				WARN("Read IP:%s Times:%hu Check:%hu usetime:%d",checkinfo.ip,
					checkinfo.seqinfo.flag.id,
					checkinfo.seqinfo.flag.check,usetime);
#endif				
				SETIPPINGCHECK::iterator it=checklist.find(checkinfo);
				if(it!=checklist.end()){
					if(tmpstatus.find(it->ip)==tmpstatus.end()){
						PINGLOSSINFOTMP lossinfo;
						memset(&lossinfo,0,sizeof(PINGLOSSINFOTMP));
						tmpstatus.insert(std::pair<std::string,PINGLOSSINFOTMP>(it->ip,lossinfo));
					}
					MAPPINGLOSSINFOTMP::iterator itinfo=tmpstatus.find(it->ip);
					if(it->seqinfo.flag.id<flag.PingTimes){
						itinfo->second.ping[it->seqinfo.flag.id]=usetime;
					}else{
						WRONG("不应出现此错误LossApi:%s id:%d",it->ip,it->seqinfo.flag.id);
					}
//移除该节点					
					checklist.erase(it);
					sendflag=1;
                }else{
#if SHOW_DEBUF_INFO				
				WARN("Not Find IP:%s Times:%hu Check:%hu",checkinfo.ip,
					checkinfo.seqinfo.flag.id,
					checkinfo.seqinfo.flag.check);
#endif					
				}
            }else
			if(usetime==-3){
				WRONG("接收异常 在GetLossInfoByIPList");
				sendtimes=flag.PingTimes;
				bStopRunning=1;
				usetime=-11;
			}	
		}while(usetime!=-11);	
//各种时间确认			
		struct timeval timestamp;
		memset(&timestamp,0,sizeof(struct timeval));
		gettimeofday(&timestamp,NULL);
//检测是否发生下一次				
		if(GetTimerSubCommon(timestamp,sendtimestamp)>flag.PerTime){
			sendflag=1;
			sendtimestamp=timestamp;
//超过1s进行一次检查		
			if(GetTimerSubCommon(timestamp,checktimestamp)>1000){
				checkflag=1;
				checktimestamp=timestamp;
			}
//检查下次构建周期			
			if(GetTimerSubCommon(timestamp,maketimestamp)>flag.PerListTime){
				maketimestamp=timestamp;
				if((IPListTmp.empty()==true)&&(sendtimes<flag.PingTimes)){
					makeflag=1;
					INFO("PING MakeLIST Ready CheckID:%hhu Size:%zd 进度:%d MakeTime:%ld.%ld SendTime:%ld.%ld",
					flag.CheckID,IPListTmp.size(),sendtimes,
					maketimestamp.tv_sec,maketimestamp.tv_usec,
					sendtimestamp.tv_sec,sendtimestamp.tv_usec);
				}
			}
		}
		if(checkflag){
			checkflag=0;
			INFO("进行一次PING超时检测 CheckID:%hhu 发送剩余:%zd EMPTY:%d 发送进度:%d 等待数量:%zd "
				"LastMakeTime:%ld.%ld LastSendTime:%ld.%ld CheckTime:%ld.%ld",
				flag.CheckID,IPListTmp.size(),IPListTmp.Empty(),
				sendtimes,checklist.size(),
				maketimestamp.tv_sec,maketimestamp.tv_usec,
				sendtimestamp.tv_sec,sendtimestamp.tv_usec,
				checktimestamp.tv_sec,checktimestamp.tv_usec);
			SETIPPINGCHECK checklistbak=checklist;
			checklist.clear();
			struct timeval timestamp=checktimestamp;
			SETIPPINGCHECK::iterator it=checklistbak.begin();
			while(it!=checklistbak.end()){
				int checktime=GetTimerSubCommon(timestamp,it->sendtime);
				if(checktime<flag.TimeOut){
					checklist.insert(*it);
				}else{
#if SHOW_DEBUF_INFO						
					WARN("TimeOut IP:%s Times:%hu Check:%hu",it->ip,
					it->seqinfo.flag.id,it->seqinfo.flag.check);
#endif					
					if(tmpstatus.find(it->ip)==tmpstatus.end()){
						PINGLOSSINFOTMP lossinfo;
						memset(&lossinfo,0,sizeof(PINGLOSSINFOTMP));
						tmpstatus.insert(std::pair<std::string,PINGLOSSINFOTMP>(it->ip,lossinfo));	
                    }
				}
				it++;
            }
			if(EnablePingThread()==0||bStopRunning==1){
				INFO("收到停止信号 Loss CheckID:%hhu 退出",flag.CheckID);
				break;
			}
        }
    }
	sock.CloseSocket();
	return GetLossStatusByTmp(tmpstatus,flag.PingTimes);
}
#include<stdlib.h>
int PINGCMP(const void*p1,const void*p2){
    int val1=*(int*)p1;
    int val2=*(int*)p2;
    if(val1<val2)return -1;
    if(val1>val2)return 1;
    return 0;
}
#if SHOW_DEBUF_INFO
void ShowIntList(int*data,int num,const char*ip,int flag){
    int i;
    printf("ip:%s flag:%d ",ip,flag);
    for(i=0;i<num;i++){
		printf("%4d",data[i]);
    }
    printf("\n");
}
#endif
#include<math.h>
MAPLOSSINFO GetLossStatusByTmp(MAPPINGLOSSINFOTMP tmpstatus,int MAX_PING_TIMES){
    MAPLOSSINFO status;
    MAPPINGLOSSINFOTMP::iterator it=tmpstatus.begin();
    while(it!=tmpstatus.end()){
		LOSSINFO        lossinfo={0};
		PINGLOSSINFOTMP tmpinfo=it->second;
		int   alivenum=0;
		int   sumtime=0;
		long  totaltime=0;
		int   i=0;
#if SHOW_DEBUF_INFO		
		ShowIntList(tmpinfo.ping,MAX_PING_TIMES,it->first.c_str(),0);
		qsort(tmpinfo.ping,MAX_PING_TIMES,sizeof(int),PINGCMP);
		ShowIntList(tmpinfo.ping,MAX_PING_TIMES,it->first.c_str(),1);
#else
		qsort(tmpinfo.ping,MAX_PING_TIMES,sizeof(int),PINGCMP);
#endif		
		int StartPos=0;
		int EndPos=MAX_PING_TIMES;
		if(MAX_PING_TIMES>16){
			StartPos=2;
			EndPos=MAX_PING_TIMES-2;
		}else
		if(MAX_PING_TIMES>10){
			StartPos=1;
			EndPos=MAX_PING_TIMES-1;
        }
		for(i=StartPos;i<EndPos;i++){
			int time=tmpinfo.ping[i];
			if(time>0){
				alivenum++;
				sumtime+=time;
				if((lossinfo.pingmin==0)||(lossinfo.pingmin>time)){
					lossinfo.pingmin=time;
                }
				if(lossinfo.pingmax<time){
					lossinfo.pingmax=time;
                }
            }
        }
		lossinfo.pingdrop=(EndPos-StartPos-alivenum)*100/(EndPos-StartPos);
		if(alivenum>0){
			lossinfo.pingavg=sumtime/alivenum;
			for(i=StartPos;i<EndPos;i++){
				int time=tmpinfo.ping[i];
				if(time>0){
					totaltime=totaltime+(time-lossinfo.pingavg)*(time-lossinfo.pingavg);
                }
            }
			lossinfo.pingstd=sqrt(1.0*totaltime/alivenum);
        }
		status.insert(std::pair<std::string,LOSSINFO>(it->first.c_str(),lossinfo));
		it++;
    }
    return status;
}
#if USE_PUBLIC_XLS
#include"PublicXls.h"
#endif
void ShowLossStatusInfo(MAPLOSSINFO IPList,bool bSave,bool bShow){
    MAPLOSSINFO::iterator it=IPList.begin();
#if USE_PUBLIC_XLS	
    PublicXls xlsfile;
    if(bSave){
		char FileName[32]={0};
		sprintf(FileName,"PingInfo_%ld.xls",time(NULL));
		xlsfile.Xls_CreateFile(FileName);
		xlsfile.Xls_CreateSheet("PING");
		xlsfile.Xls_WriteRawData("ssssss","IP","AVG","MIN","MAX","STD","DROP");
    }
#endif	
    while(it!=IPList.end()){
#if USE_PUBLIC_XLS		
		if(bSave){
			xlsfile.Xls_WriteRawData("sdddfd",it->first.c_str(),
			it->second.pingavg,it->second.pingmin,it->second.pingmax,
			it->second.pingstd,it->second.pingdrop);
        }
#endif		
		if(bShow){
			if(it->second.pingavg>0&&it->second.pingdrop<20){
				INFO("IP:%s (avg/min/max)/(%d/%d/%d)ms std:%0.2f drop:%d%%",it->first.c_str(),
				it->second.pingavg,it->second.pingmin,it->second.pingmax,it->second.pingstd,it->second.pingdrop);
			}else{
				WARN("IP:%s (avg/min/max)/(%d/%d/%d)ms std:%0.2f drop:%d%%",it->first.c_str(),
				it->second.pingavg,it->second.pingmin,it->second.pingmax,it->second.pingstd,it->second.pingdrop);
            }
        }
		it++;
    }
#if USE_PUBLIC_XLS	
    if(bSave){
		xlsfile.Xls_CloseSheet();
		xlsfile.Xls_CloseFile();
    }
#endif	
}
#endif
